package com.tagit.sphinx4;

import java.net.URL;

import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.IContext;
import org.slf4j.Logger;

import com.tagit.speech.SpeechRecognizer;
import com.tagit.speech.SpeechSession;

import edu.cmu.sphinx.recognizer.Recognizer;
import edu.cmu.sphinx.recognizer.Recognizer.State;
import edu.cmu.sphinx.recognizer.StateListener;
import edu.cmu.sphinx.result.Result;
import edu.cmu.sphinx.util.props.ConfigurationManager;
import edu.cmu.sphinx.util.props.PropertyException;
import edu.cmu.sphinx.util.props.PropertySheet;

public class SphinxRecognizer extends SpeechRecognizer implements Runnable,
		StateListener {
	final private Logger log = Red5LoggerFactory.getLogger(this.getClass(),
			"flashMic");
	protected Recognizer recognizer;
	public ExternalMicrophone microphone;
	protected ConfigurationManager cm;

	private String lang;
	private int sampleRate;
	private SpeechSession session;

	public SphinxRecognizer(SpeechSession session, IContext context,
			String lang, int sampleRate) {
		try {
			// TODO: move lang settings to external XML
			this.session = session;
			this.lang = lang;
			this.sampleRate = sampleRate;

			String configFile = "config/sphinx4." + lang + ".xml";

			URL configURL = context.getResource(configFile).getURL();

			log.info("Configuration URL " + configURL);
			cm = new ConfigurationManager(configURL);

			microphone = (ExternalMicrophone) cm.lookup("microphone");
			microphone.setSampleRate(sampleRate);

			// allocate the resource necessary for the recognizer
			recognizer = (Recognizer) cm.lookup("recognizer");
			log.info("Allocating Sphinx Recognizer");
			recognizer.addStateListener(this);
			recognizer.allocate();

			Thread recThread = new Thread(this);
			recThread.start();
		} catch (Exception e) {
			log.error("Problem configuring Recognizer: ", e);
		}
	}

	@Override
	public void run() {
		while (true) {
			// log.debug("1 Recognizing " + isRecognizing);
			if (isRecognizing) {
				log.info("Attempting Recognition");
				log.info(recognizer.getState().name());

				Result result = recognizer.recognize();
				log.info("Recognition Result " + result);
				log.debug("Recognizing " + isRecognizing);
				log.info(recognizer.getState().name());
				if (result != null) {
					log.info("Recognition Result Found");
					String input = result.getBestResultNoFiller();
					log.info(input);
					session.RecognizedInput(input);
				} else {
					try {
						log.info(recognizer.getState().name());
						while (!isRecognizing)
							Thread.sleep(10);
					} catch (InterruptedException e) {
						log.debug("Thread Error", e);
						// do nothing
					}

				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.tagit.speech.SpeechRecognizer#readData(double[], int, int)
	 */
	@Override
	public void readData(double[] data, int collectTime) {
		microphone.readData(data, collectTime);
	}

	public String getLang() {
		return lang;
	}

	public int getSampleRate() {
		return sampleRate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * edu.cmu.sphinx.recognizer.StateListener#statusChanged(edu.cmu.sphinx.
	 * recognizer.Recognizer.State)
	 */
	@Override
	public void statusChanged(State status) {
		switch (status) {
		case READY:
		case ALLOCATED:
			session.setRecognizerReady(true);
			break;
		case DEALLOCATED:
		case ERROR:
			session.setRecognizerReady(false);
			break;		
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * edu.cmu.sphinx.util.props.Configurable#newProperties(edu.cmu.sphinx.util
	 * .props.PropertySheet)
	 */
	@Override
	public void newProperties(PropertySheet ps) throws PropertyException {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.tagit.speech.SpeechRecognizer#close()
	 */
	@Override
	public void close() {
		stopRecognition();
		recognizer.deallocate();
	}
}
